package edu.kufpg.armatus; import android.app.Activity; import android.app.Application; import android.os.Bundle; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; /** * This class manages the connection between an application's {@link Activity Activities} * and the {@link AsyncActivityTask AsyncActivityTasks} that they spawn. */ public class BaseApplication extends Application { /** * Manages the application's references to {@link AsyncActivityTask AsyncActivitiyTasks} * automatically by tracking each {@link Activity}'s lifecycle. This requires API 14 * or greater. */ private static class TaskCallbacks implements ActivityLifecycleCallbacks { private final BaseApplication mApp; public TaskCallbacks(BaseApplication app) { mApp = app; } @Override public void onActivityResumed(Activity activity) { mApp.attachActivity(activity); } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { mApp.detachActivity(activity); } // Unneeded methods @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {} @Override public void onActivityStarted(Activity activity) {} @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} @Override public void onActivityDestroyed(Activity activity) {} }; @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new TaskCallbacks(this)); } /** * An {@link Activity} can spawn any number of {@link android.os.AsyncTask AsyncTasks} * simultaneously, so use a {@link ListMultimap} to connect an {@code Activity}'s name * and its {@link AsyncActivityTask AsyncActivityTasks}. */ private final ListMultimap<String, AsyncActivityTask<? extends Activity,?,?,?>> mActivityTaskMap = ArrayListMultimap.create(); /** * Removes unused {@link AsyncActivityTask AsyncActivityTasks} after they have * completed execution. * @param activity The {@link Activity} that spawned the {@code AsyncActivityTask}. * @param task The {@code AsyncActivityTask} to remove. */ public <A extends Activity> void removeTask(A activity, AsyncActivityTask<A,?,?,?> task) { mActivityTaskMap.remove(activity.getClass().getName(), task); } /** * Establishes a connection between an {@link Activity} and a {@link AsyncActivityTask} * that will persist through device rotation or standby. * @param activity The {@code Activity} that spawned the {@code AsyncActivityTask}. * @param task The {@code AsyncActivityTask} to connect. */ public <A extends Activity> void addTask(A activity, AsyncActivityTask<A,?,?,?> task) { mActivityTaskMap.put(activity.getClass().getName(), task); } /** * While an {@link Activity} rotates or is in standby, attempting to call an {@code Activity} * method from one of its {@link AsyncActivityTask AsyncActivityTasks} can produce * unexpected results. Use this method to set all of an {@code Activity}'s references * in its tasks to {@code null} so that the tasks can work around rotation or standby. * @param activity The {@code Activity} whose references should be set to null. */ public void detachActivity(Activity activity) { for (AsyncActivityTask<? extends Activity,?,?,?> task : mActivityTaskMap.get(activity.getClass().getName())) { task.detachActivity(); } } /** * Reestablishes the connection between an {@link Activity} and its {@link AsyncActivityTask * AsyncActivityTasks} after the {@code Activity} is resumed. * @param activity The {@code Activity} whose references should be reestablished. */ public <A extends Activity> void attachActivity(A activity) { for (AsyncActivityTask<? extends Activity,?,?,?> task : mActivityTaskMap.get(activity.getClass().getName())) { @SuppressWarnings("unchecked") AsyncActivityTask<A,?,?,?> castTask = (AsyncActivityTask<A,?,?,?>) task; castTask.attachActivity(activity); } } public void cancelTasks(Activity activity) { for (AsyncActivityTask<? extends Activity,?,?,?> task : mActivityTaskMap.get(activity.getClass().getName())) { task.cancel(true); } } }